package com.enzo.prms.common.base.dao;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;

import com.enzo.prms.common.base.to.BaseEntity;
import com.enzo.prms.common.exception.base.DAOException;
import com.enzo.prms.common.util.DateUtil;

public class BaseDAOImpl<T extends BaseEntity> implements BaseDAO<T> {
    private static final Logger log = Logger.getLogger(BaseDAOImpl.class);
    protected HibernateTemplate hibernateTemplate;
    private Class<? extends BaseEntity> entityClass;

    @SuppressWarnings("unchecked")
	public BaseDAOImpl() {
		Type localType = getClass().getGenericSuperclass();
		if ((localType instanceof ParameterizedType)) {
			this.entityClass = (Class<T>) ((ParameterizedType) this.getClass()
					.getGenericSuperclass()).getActualTypeArguments()[0];
		} else {
			this.entityClass = (Class<T>) BaseEntity.class;
		}
	}
   
    /**
     * 获得DAO类对应的实体类型，所有继承者必须实现该方法并给出相应的实体类型。
     */
	protected Class<? extends BaseEntity> getEntityClass() {
		return entityClass;
	}


	@Override
	public void save(T obj) {
		BaseEntity baseTO = (BaseEntity) obj;
		baseTO.setCreateBy("admin");
		baseTO.setUpdateBy("admin");
		baseTO.setFlag(1);
		baseTO.setCreateTs(DateUtil.getTimestamp());
		baseTO.setUpdateTs(DateUtil.getTimestamp());
		hibernateTemplate.save(baseTO);
	}

    @Override
	public void update(T obj)throws DAOException {
        try {
            BaseEntity baseTO = (BaseEntity) obj;
            baseTO.setUpdateBy("admin");
            baseTO.setUpdateTs(DateUtil.getTimestamp());
            hibernateTemplate.update(obj);
        } catch (Exception e) {
            log.error(this, e);
            throw new DAOException(e);
        }
    }

    @Override
	public void delete(T obj) {
    	if(obj != null)
        hibernateTemplate.delete(obj);
    }

    @Override
	public void delete(Integer id){
        T obj = getById(id);
        if (obj != null) {
            delete(obj);
        }
    }

    @Override
	public void delete(Collection<T> entities) {
		hibernateTemplate.deleteAll(entities);
	}

	@SuppressWarnings("unchecked")
	@Override
    public T getById(Integer id) {
        return (T) hibernateTemplate.get(entityClass, id);
    }

    @SuppressWarnings("unchecked")
	@Override
    public T load(Integer id) {
        return (T) hibernateTemplate.load(entityClass, id);
    }
    
    @SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public void executeByHql(final String hql, final Map<String, Object> params)throws DAOException {
		try {
	    	hibernateTemplate.execute(new HibernateCallback() {
				@Override
				public Object doInHibernate(Session session)
						throws HibernateException, SQLException {
					Query query = session.createQuery(hql);
	                if(params != null){
	                	Iterator<String> keyIterator = params.keySet().iterator();
	                    while (keyIterator.hasNext()) {
	                        String key = keyIterator.next();
	                        Object obj=params.get(key);
	                        //这里考虑传入的参数是什么类型，不同类型使用的方法不同  
	                        if(obj instanceof Collection<?>){  
	                            query.setParameterList(key, (Collection<?>)obj);  
	                        }else if(obj instanceof Object[]){  
	                            query.setParameterList(key, (Object[])obj);  
	                        }else{  
	                            query.setParameter(key, obj);  
	                        }
	                    }
	                }
	                query.executeUpdate();
					return null;
				}
			});
		} catch (Exception e) {
			throw new DAOException(e);
		}
	}
    

	@SuppressWarnings("unchecked")
	@Override
	public List<T> getListByCriteria(final DetachedCriteria criteria)throws DAOException{
		return (List<T>)hibernateTemplate.findByCriteria(criteria);
	}

	@Override
	@SuppressWarnings({ "unchecked", "rawtypes" })
    public List<T> getListForPage(final String sql, final Map<String, Object> params,final int offset,final int length) {
        return (List<T>) hibernateTemplate.execute(new HibernateCallback() {
            public Object doInHibernate(Session session)
                    throws HibernateException, SQLException {
                Query query = session.createQuery(sql);
                if(params != null){
                	Iterator<String> keyIterator = params.keySet().iterator();
                    while (keyIterator.hasNext()) {
                        String key = keyIterator.next();
                        query.setParameter(key, params.get(key));
                    }
                }
                query.setFirstResult(offset);
                query.setMaxResults(length);
                return query.list();
            }
        });
    }
    
    @SuppressWarnings({ "unchecked", "rawtypes" })
   	public long count(final String sql) {
   		return (Long) hibernateTemplate.execute(new HibernateCallback() {
   			public Object doInHibernate(Session session)
   					throws HibernateException, SQLException {
   				return (Long) session.createQuery(sql).uniqueResult();
   			}
   		});
   	}

    @Override
	@SuppressWarnings({ "unchecked", "rawtypes" })
    public List<T> getListByHQL(final String hql, final Map<String, Object> params) {
        return (List<T>)hibernateTemplate.execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session) throws HibernateException,
					SQLException {
				Query query=session.createQuery(hql);
				if(params != null){
                	Iterator<String> keyIterator = params.keySet().iterator();
                    while (keyIterator.hasNext()) {
                        String key = keyIterator.next();
                        query.setParameter(key, params.get(key));
                    }
                }
				return query.list();
			}
        	
		});
    }

    @Override
	@SuppressWarnings({ "unchecked", "rawtypes" })
    public List<T> getListBySQL(final String sql, final Map<String, Object> params) {
        return (List<T>) hibernateTemplate.execute(new HibernateCallback() {
            public Object doInHibernate(Session session)
                    throws HibernateException, SQLException {
                Query query = session.createSQLQuery(sql).addEntity(entityClass);
                if(params != null){
                	Iterator<String> keyIterator = params.keySet().iterator();
                    while (keyIterator.hasNext()) {
                        String key = keyIterator.next();
                        query.setParameter(key, params.get(key));
                    }
                }
                return query.list();
            }
        });
    }

    @Override
	@SuppressWarnings({ "unchecked", "rawtypes" })
    public List<T> getListByNamedSQL(final String sqlName,
                                     final Map<String, Object> params) {
        return (List<T>) hibernateTemplate.execute(new HibernateCallback() {
            public Object doInHibernate(Session session)
                    throws HibernateException, SQLException {
                Query query = session.getNamedQuery(sqlName);
                if(params != null){
                	Iterator<String> keyIterator = params.keySet().iterator();
                    while (keyIterator.hasNext()) {
                        String key = keyIterator.next();
                        query.setParameter(key, params.get(key));
                    }
                }
                return query.list();
            }
        });
    }
    
    /**
     * 组成存储过程调用语句。
     * 
     * @param spName
     * @param parameters
     * @return
     */
    protected String concatSpSql(String spName, Map<String, Object> parameters) {
            StringBuilder sb = new StringBuilder();
            sb.append("{call ").append(spName).append(" (");
            if (parameters != null) {
                    for (int i = 0; i < parameters.size(); i++) {
                            if (i != 0) {
                                    sb.append(",");
                            }
                            sb.append("?");
                    }
                    sb.append(")}");
            }
            return sb.toString();
    }

    @Override
	public String getDatabaseInfo() {
    	StringBuilder buf = new StringBuilder();
        Connection conn = hibernateTemplate.getSessionFactory().getCurrentSession().connection();
        try {
                buf.append(conn.getMetaData().getDatabaseProductName()).append(" ");
                buf.append(conn.getMetaData().getDatabaseProductVersion()).append(" ");
                buf.append(conn.getMetaData().getDatabaseMajorVersion()).append(" ");
//              buf.append(this.getSession().connection().getMetaData().getDatabaseMinorVersion());
        } catch (SQLException e) {
                e.printStackTrace();
                return "[Unknown]";
        }
        return buf.toString();
	}

	@Override
	public boolean checkDatabaseAvailable() {
		throw new UnsupportedOperationException("Not supported yet.");
	}

	@Override
	public void execute(String spName) throws DAOException {
		String sql = "{call " + spName + " ()}";
        try {
                CallableStatement cstmt = this.getSession().connection().prepareCall(sql);
                cstmt.execute();
        } catch (Throwable e) {
                throw new DataAccessResourceFailureException("执行存储过程" + spName + "失败", e);
        }		
	}

	@Override
	public void execute(String spName, Map<String, Object> parameters)
			throws DAOException {
		String sql = concatSpSql(spName, parameters);
        try {
                CallableStatement cstmt = this.getSession().connection().prepareCall(sql);
                cstmt.execute();
        } catch (Throwable e) {
                throw new DataAccessResourceFailureException("执行存储过程" + spName + "失败", e);
        }
	}



	@Override
	public Map<String, Object> executeWithResult(String spName,
			Map<String, Object> parameters, Map<String, Integer> outParams,
			String cursorName) throws DAOException {
		String sql = concatSpSql(spName, parameters);
        Map<String, Object> ret = new HashMap<String, Object>();
        try {
                CallableStatement cs = this.getSession().connection().prepareCall(sql);
                ParameterMetaData pmd = cs.getParameterMetaData();
                int pcount = pmd.getParameterCount();
                for (int i = 0; i < pcount; i++) {
                        // getParameterMode() method is not supported by recent Oracle JDBC driver.
                        int mode = pmd.getParameterMode(i + 1);
                        int type = pmd.getParameterType(i + 1);
                        if (mode == ParameterMetaData.parameterModeOut || mode == ParameterMetaData.parameterModeInOut) {
                                cs.registerOutParameter(i + 1, type);
                        }
                }
                if (parameters != null) {
                        Iterator it = parameters.entrySet().iterator();
                        for (int i = 1; it.hasNext(); i++) {
                                Object key = it.next();
                                Object value = parameters.get(key);
                                cs.setObject(key.toString(), value, JdbcHelper.translateType(value));
                        }
                }
                ResultSet rs = cs.executeQuery();
                while (rs.next()) {
                        int count = rs.getMetaData().getColumnCount();
                        for (int i = 0; i < count; i++) {
                                String name = rs.getMetaData().getColumnName(i + 1);
                                Object o = rs.getObject(i + 1);
                                ret.put(name, o);
                        }
                }
        } catch (Throwable e) {
                throw new DataAccessResourceFailureException("执行存储过程" + spName + "失败", e);
        }
        return ret;
	}


	@Override
	public Map<String, Object> executeWithResult(
			com.mysql.jdbc.Connection conn, String spName,
			Map<String, Object> parameters, Map<String, Integer> outParams,
			String cursorName) throws DAOException {
		throw new RuntimeException("Not implemented yet");
	}



	@Override
	public List<Object> executeWithResultset(String spName) throws DAOException {
		throw new RuntimeException("Not implemented yet");
	}



	@Override
	public List<Object> executeWithResultset(String spName,
			Map<String, Object> parameters) throws DAOException {
		throw new RuntimeException("Not implemented yet");
	}
    
    
    private Session getSession(){
    	return hibernateTemplate.getSessionFactory().getCurrentSession();
    }

    public HibernateTemplate getHibernateTemplate() {
        return hibernateTemplate;
    }

    @Resource
    public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
        this.hibernateTemplate = hibernateTemplate;
    }

}
